/**************************************************************************
 * @name: RoundShow_MMap.c
 * @brief: 这是用于采用内存映射技术访问ARM开发板的LCD屏
 * @date: 2025/01/07
 * @author: boluzs@163.com
 * @note:
 *          1. 圆的圆心(a,b)，半径r
 *          2. 圆的方程(x-a)^2+(y-b)^2=r^2
 *          3. 数组buf渲染区域(x-a)^2+(y-b)^2<=r^2
 *          3. buf[800*i+j]
 * @version: 1.0.0
 * @Copyright (c) 2025 by boluzs@163.com, All Rights Reserved.
 **************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

// 为了提高程序的可移植性，LCD的设备文件的路径通过宏定义实现
#define LCD_DEV "/dev/fb0"
#define LCD_MAPSIZE 800 * 480 * 4
#define WHITE 0xFFFFFF
#define RED 0xFF0000

int main()
{
    // 1.打开LCD，linux系统下把硬件设备抽象为文件，所以可以访问硬件设备的设备文件
    int lcd_fd = open(LCD_DEV, O_RDWR);

    // 错误处理
    if (-1 == lcd_fd)
    {
        fprintf(stderr, "open lcd error,errno = %d,%s\n", errno, strerror(errno));
        exit(-1);
    }

    // 2.对LCD屏进行内存映射
    unsigned int *lcd_mp = (unsigned int *)mmap(
        NULL,                   // 由系统内核选择合适的位置
        LCD_MAPSIZE,            // 指的是要映射的内存大小
        PROT_READ | PROT_WRITE, // 以可读可写的方式访问
        MAP_SHARED,             // 选择共享映射空间
        lcd_fd,                 // 指的是待映射的文件描述符
        0                       // 不偏移内存
    );

    if (lcd_mp == MAP_FAILED)
    {
        fprintf(stderr, "mmap for lcd error,errno = %d,%s\n", errno, strerror(errno));
        exit(-1);
    }

    // 3.准备颜色数据 比如白色在RGB颜色对照表中对应的值是 0xFFFFFF
    int x = 400, y = 240, r = 10;
    for (int i = 0; i < 480; i++)
    {
        for (int j = 0; j < 800; j++)
        {
            if ((j - x) * (j - x) + (i - y) * (i - y) <= r * r)
            {
                *(lcd_mp + (800 * i + j)) = WHITE;
            }
            else
            {
                *(lcd_mp + (800 * i + j)) = 0x0000FF;
            }
        }
    }

    // 4.关闭LCD并解除映射,不会影响已经写入到LCD的设备文件中颜色数据
    close(lcd_fd);
    munmap(lcd_mp, LCD_MAPSIZE);

    return 0;
}